#include "../common/common.h"

#ifndef __HOF_HEADER__
#define __HOF_HEADER__ 
// 高斯卷积核
static cv::Mat getGaussianKernel(int n, double sigma,int ktype) 
{
  const int SMALL_GAUSSIAN_SIZE = 7 ;
  static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE]=
    {
      {1.f},
      {0.25f, 0.5f, 0.25f},
      {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
      {0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
    };
  
  const float * fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ? small_gaussian_tab[n>>1] : NULL ;
  cv::Mat kernel(n,1,ktype) ;
  float * cf = (float*)kernel.data ;
  double * cd = (double*)kernel.data ;
  
  double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5-1)*0.3 + 0.8 ; 
  double scale2X = -0.5/(sigmaX*sigmaX) ;
  double sum = 0 ;
  
  for( int index= 0 ; index < n ; ++ index )
    {
      double x = index - (n-1)*0.5 ;
      double t = fixed_kernel ? (double)fixed_kernel[index]:std::exp(scale2X*x*x) ;
      if( ktype == CV_32F )
	{
	  cf[index] = (float) t ;
	  sum += cf[index] ;
	}
      else
	{
	  cd[index] = t ;
	  sum += cd[index] ;
	}
    }
  sum = 1./sum ;
  for( int index = 0 ; index < n ; ++ index )
    {
      if( ktype == CV_32F )
	cf[index] = (float)(cf[index]*sum) ;
      else cd[index] *= sum ;
    }
  return kernel ;
}
  static void convertImageto8U(IplImage* InputImage, IplImage* OutputImage)
{
  float max = -10000000;
  float min = 10000000 ;
  if( InputImage == NULL || OutputImage == NULL ) return ;
  uchar* InputData = (uchar*)InputImage->imageData ;
  float* InputPoint = NULL ;
  uchar* OutputData = (uchar*)OutputImage->imageData ;
  uchar* OutputPoint = NULL ;
  
  for( int index_row = 0 ; index_row < InputImage->height ; ++ index_row )
    {
      InputPoint = (float*)InputData;
      OutputPoint = OutputData ;
      for( int index_col = 0 ; index_col < InputImage->width ; ++ index_col )
	{
	  float value = fabs(*InputPoint) ;
	  if( value > max ) max = fabs(value) ;
	  if( value < min ) min = fabs(value) ;
	  InputPoint += 1 ;
	}
      InputData += InputImage->widthStep ;
    }
  InputData = (uchar*)InputImage->imageData ;
  for( int index_row = 0 ; index_row < InputImage->height ; ++ index_row )
    {
      InputPoint = (float*)InputData;
      OutputPoint = OutputData ;
      for( int index_col = 0 ; index_col < InputImage->width ; ++ index_col )
	{
	  float value = fabs(*InputPoint) ;
	  *OutputPoint = (uchar)((value-min)/(max-min)*255);
	  InputPoint += 1 ;
	  OutputPoint += 1 ;
	}
      InputData += InputImage->widthStep ;
      OutputData += OutputImage->widthStep ;
    }
}
/*
  @计算当前视频流像素点的光流直方图
  Lucas–Kanade算法：它计算两帧在时间t 到t + δt之间每个每个像素点位置的移动。 由于它是基于图像信号的泰勒级数，这种方法称为差分，这就是对于空间和时间坐标使用偏导数。
 */
class HistogramOfFlow
{
 public:
  HistogramOfFlow(const char* _videoFileName, const char* _stipFileName, const char* _featuresVectorFileName);
  ~HistogramOfFlow();
  // 从视频中得到相应的特征向量
  void getFeaturesFromVideo() ;
  // 私有的函数
 private:
  // 计算特征
  void calculateFeatures() ;
  // 当前队列中帧数不够时，压10帧
  // 返回压入的帧数
  unsigned pushImage(CvCapture* _capture) ;
  // 得到子图
  IplImage * GetSubImage(IplImage* _src, cv::Point points, CvSize _size) ;
  // 计算空间上梯度图 
  void calculateSpaceGradient(IplImage* _src, IplImage* _xSpaceDst, IplImage* _ySpaceDst) ;
  // 计算时间上梯度图
  void calculateTimeGradient(IplImage* _ansImage, IplImage* _nextImage,IplImage* _retImage) ;
  // 计算梯度矩阵 x梯度图，y梯度图，t梯度图1组，共3组图像9幅图像
  void calculateGradient(std::vector<IplImage*> &_inImageList,std::vector<IplImage*> &_outImageList) ;
  // 通过梯度图计算光流图，光流的方向和光流的强度
  void calculateOpticFlow(std::vector<IplImage*> &_inImageList,IplImage* _orientImage, IplImage* _velocityImage) ;
  // 在m*m*m的立体上尽心高斯平滑
  void gaussian3DSmooth(IplImage* _ansImage, IplImage* _nowImage, IplImage* _nextImage) ;
  // 求得2*2的逆矩阵
  void get2X2InverseMatrix(float _matrix[2][2]) ;
  // 统计结果高斯权重
  void staticFeaturesGaussian(std::vector<float> &_retFeature, IplImage* _orientImage, IplImage* _velocityImage) ; 
  // 创新点 使用梯度来进行计算
  void staticFeaturesMy(std::vector<float> &_retFeature, IplImage* _orientImage, IplImage* _velocityImage, IplImage * _xSpaceImage, IplImage* _ySpaceImage) ;
  // 初始化权重
  void init() ;
 private:

  const float PI ;
  // 统计光流信息的梯度阈值
  const float gradientThreshold ;
  // 计算光流的窗口大小
  // winSize 32 * 32 , blockSize 16 * 16 ;
  CvSize winSize, blockSize ;
  // 视频文件名
  std::string videoFileName ; 
  // stip时空兴趣点文件名
  std::string stipFileName ;
  // 特征保存文件 
  std::string featuresVectorFileName ;
  // 存储视频序列
  std::list< std::pair<int,IplImage*> > imageList ; 
  // 分布矩阵，进行统计的时候用
  
  struct PixData
  {
    float weight[4] ;
    int offset[4] ;
  };
  std::vector<PixData> pixData ;
};

#endif 
